using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.InteropServices; 
using System.Reflection;
using MineWorld;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;

namespace StateMasher
{
    /// <summary>
    /// This is the main type for your game
    /// </summary>
    public class StateMachine : Microsoft.Xna.Framework.Game
    {
        [DllImport("user32.dll")]
        public static extern int GetForegroundWindow(); 

        public GraphicsDeviceManager graphicsDeviceManager;
        public PropertyBag propertyBag = null;

        private string currentStateType = "";
        public string CurrentStateType
        {
            get { return currentStateType; }
        }

        private State currentState = null;
        private bool needToRenderOnEnter = false;

        private FPSCounter fpsCounter = new FPSCounter();
        public double FrameRate
        {
            get { return fpsCounter.fps(); }
        }

        private MouseState msOld;

        public StateMachine()
        {
            Content.RootDirectory = "Content";
            graphicsDeviceManager = new GraphicsDeviceManager(this);
            EventInput.EventInput.Initialize(this.Window);
            EventInput.EventInput.CharEntered += new EventInput.CharEnteredHandler(EventInput_CharEntered);
            EventInput.EventInput.KeyDown += new EventInput.KeyEventHandler(EventInput_KeyDown);
            EventInput.EventInput.KeyUp += new EventInput.KeyEventHandler(EventInput_KeyUp);
        }

        protected void ChangeState(string newState)
        {
            Debug.Assert(newState != "","Newstate string at changestate was empty\n\r Report at dev");

            // Call OnLeave for the old state.
            if (currentState != null)
                currentState.OnLeave(newState);

            // Instantiate and set the new state.
            Assembly a = Assembly.GetExecutingAssembly();
            Type t = a.GetType(newState);
            currentState = Activator.CreateInstance(t) as State;

            // Set up the new state.
            currentState._P = propertyBag;
            currentState._SM = this;
            currentState.OnEnter(currentStateType);
            currentStateType = newState;
            needToRenderOnEnter = true;
        }

        public bool WindowHasFocus()
        {
            return GetForegroundWindow() == (int)Window.Handle;
        }

        protected override void Initialize()
        {
            base.Initialize();
        }

        //Keyboard input
        public void EventInput_CharEntered(object sender, EventInput.CharacterEventArgs e)
        {
            if (currentState != null)
                currentState.OnCharEntered(e);
        }

        public void EventInput_KeyDown(object sender, EventInput.KeyEventArgs e)
        {
            if (currentState != null)
                currentState.OnKeyDown(e.KeyCode);
        }

        public void EventInput_KeyUp(object sender, EventInput.KeyEventArgs e)
        {
            if (currentState != null)
                currentState.OnKeyUp(e.KeyCode);
        }

        protected override void LoadContent()
        {
            
        }

        protected override void UnloadContent()
        {
        }

        protected override void Update(GameTime gameTime)
        {
            //If we dont got a property bag or state then return silent
            if (currentState == null && propertyBag == null)
            {
                return;
            }

            // Call OnUpdate.
            string newState = currentState.OnUpdate(gameTime, Keyboard.GetState(), Mouse.GetState());
            if (newState != null)
                ChangeState(newState);

            // Check for mouse events.
            MouseState msNew = Mouse.GetState();
            if (WindowHasFocus())
            {
                if (msOld.LeftButton == ButtonState.Released && msNew.LeftButton == ButtonState.Pressed)
                    currentState.OnMouseDown(MouseButtons.LeftButton, msNew.X, msNew.Y);
                if (msOld.MiddleButton == ButtonState.Released && msNew.MiddleButton == ButtonState.Pressed)
                    currentState.OnMouseDown(MouseButtons.MiddleButton, msNew.X, msNew.Y);
                if (msOld.RightButton == ButtonState.Released && msNew.RightButton == ButtonState.Pressed)
                    currentState.OnMouseDown(MouseButtons.RightButton, msNew.X, msNew.Y);
                if (msOld.LeftButton == ButtonState.Pressed && msNew.LeftButton == ButtonState.Released)
                    currentState.OnMouseUp(MouseButtons.LeftButton, msNew.X, msNew.Y);
                if (msOld.MiddleButton == ButtonState.Pressed && msNew.MiddleButton == ButtonState.Released)
                    currentState.OnMouseUp(MouseButtons.MiddleButton, msNew.X, msNew.Y);
                if (msOld.RightButton == ButtonState.Pressed && msNew.RightButton == ButtonState.Released)
                    currentState.OnMouseUp(MouseButtons.RightButton, msNew.X, msNew.Y);
                if (msOld.ScrollWheelValue != msNew.ScrollWheelValue)
                    currentState.OnMouseScroll(msNew.ScrollWheelValue - msOld.ScrollWheelValue);
            }
            msOld = msNew;

            base.Update(gameTime);
        }

        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.Black);

            //If we dont got a property bag or state then return silent
            if (currentState == null && propertyBag == null)
            {
                return;
            }

            // TODO Find a better place for fps counter
            // I tottaly dont like the way how FPS is implented
            StateMachine _SM = currentState._SM;
            if ((_SM as MineWorldGame).Csettings.DrawFrameRate)
            {
                fpsCounter.frame((int)gameTime.TotalGameTime.Milliseconds + (int)gameTime.TotalGameTime.Seconds * 1000 + (int)gameTime.TotalGameTime.Minutes * 60000 + (int)gameTime.TotalGameTime.Hours * 3600000);
            }
            
            // Call OnRenderAtUpdate.
            currentState.OnRenderAtUpdate(GraphicsDevice, gameTime);

            // If we have one queued, call OnRenderAtEnter.
            if (needToRenderOnEnter)
            {
                needToRenderOnEnter = false;
                currentState.OnRenderAtEnter(GraphicsDevice);
            }
            
            base.Draw(gameTime);
        }
    }
}
